developmentor - a developer services company				  
				      Essential .NET Security
   Modules

Remoting

Estimated time for completion: 60 minutes


Goals of this exercise:

  • Get a simple remoting sample running between two machines
  • Host the server in IIS
  • Use IIS to provide authentication and note the limitations
  • Use a PrincipalPermission attribute to add authorization checks to a method
  • Download, install, and use the SSPI channel sink sample

Overview

In this activity, you'll add security to an existing remoting application. By "security" we mean authentication, authorization, message integrity, and message confidentiality. The security story in .NET remoting is still very sketchy, but this will give you an idea of what you can do today, and what is coming in a future release.


part 1- A simple remoting example

In this part, you'll run a basic remoting sample that's been written for you, just to get things working.

steps:

  1. Open up the sample solution: SecureRemoting.sln. Note that there are three projects in this solution, a client, server, and a common assembly that the client and server both reference, containing the interfaces used in the sample.

  2. Take a look at the server (server.cs). Note that there is a main entry point, so it can be run standalone. The server simply bootstraps .NET remoting from its configuration file (server.exe.config), and provides an object that the client can access via remoting.

  3. Take a look at the client (client.cs). Note that after bootstrapping .NET remoting from its config file (client.exe.config), the client makes a few calls to the server, printing out the results. Also note that the client provides an implementation of an object that the server will call back into during the experiment.

  4. While you're looking at the client file, modify the line of code where the server URI is hardcoded. If you're working with a partner (good for you), change "localhost" to be your partner's machine name or ip address.

  5. Have a quick look at common.cs to see what both the client and server depend upon (just a couple of interfaces).

  6. Finally, have a look at the client and server configuration files. These are both named "app.config" to take advantage of Visual Studio's automatic copying of config files to output directories (ask your instructor to explain if you've not heard of this nifty feature). Note that both the client and server are configured to use the HTTP channel with a binary formatter. This is a very common configuration in .NET remoting implementations.

  7. Build the project, then using a command prompt, start the server process in its own console (e.g., type "start server.exe").

  8. From your console, run the client. You should see a successful run the first time. Note that there is no authentication.

  9. Shut your server down. You're now ready to host in IIS.


part 2- Hosting in IIS

By hosting in IIS, you can hack some security onto the front end of your remoting application. We'll add authentication support in this example.

steps:

  1. Create a directory on your hard drive somewhere easy to find (e.g., c:\temp\web). Using IIS, map a virtual directory onto the file system directory you just created. Copy the server's configuration file to this directory, but name it web.config instead of server.exe.config. Open your new web.config file and remove the port attribute completely - this server isn't going to be listening on port 4000. It's going to rely on IIS to do the listening.

  2. Create a subdirectory called "bin" and copy server.exe and common.dll into this subdirectory. (You should now have a virtual root with a web.config file and a "bin" subdirectory.)

  3. Open up client.cs and change the server URI again (if you're partnering with someone, you should still point to their machine). You'll need to change the port number from 4000 to 80, since IIS is the front end to the server now. You'll also need to add the name of the virtual directory into the URI, so for instance, http://localhost:80/server.rem should become http://localhost:80/MyVirtualDir/server.rem.

  4. Rebuild your client and give it a whirl. Note that you don't need to start the server - it'll be loaded into the IIS worker process via the ASP.NET handler responsible for handling the .rem extension. This handler knows to look for a remoting configuration in web.config, and basically does the same sort of thing the server's Main() function used to do when run standalone.


part 3- Turning on IIS authentication

Now that your server is hosted in IIS, you can have IIS do some heavy lifting and authenticate your clients for you.

steps:

  1. In IIS admin, bring up the property dialog for your virtual directory, and select the Directory Security tab. Push the first button on this page to access the authentication settings.

  2. Uncheck "Enable anonymous access" and check "Integrated Windows authentication". This will force the system to authenticate every request.

  3. In the client's configuration file (app.config under the client project), add another attribute to the <channel> element: useDefaultCredentials='true'. This will allow you to use your NT logon credentials to authenticate the call. This virtually undocumented attribute is very important!

  4. Run your client again. The call from the client to the server should now be authenticated. But is the callback authenticated? Also, now that we're authenticating, is the payload encrypted? To answer the second question, you might try wiring up a trace listener so you can verify your answer.


part 4- Adding authorization

You should be able to do full-fledged role-based authentication now that you're hosted inside of IIS and ASP.NET.

steps:

  1. In server.cs, add a principal permission on the Server.DoSomethingPrivileged method. A simple example would be to demand that the client is in some local group. Here's an example of a demand for a local group called "Friends" on a machine called "IGGY":

    // need to add this using statement at the top of the file before this will work
    using System.Security.Permissions;
    
    [PrincipalPermission(SecurityAction.Demand, Role=@"IGGY\Friends")]
    void DoSomethingPrivileged() {
      // ...
    }
  2. Experiment with this until you've seen your request fail and succeed due to the authorization demand. If you have any questions, ask your instructor for help.


part 5- Using the SSPI remoting sink (optional)

This sample is unsupported by Microsoft, but you should pay attention to it as it'll give you an idea of what a real built-in solution might look like.

steps:

  1. Download and install the SSPI and remoting sink samples. Your instructor will make these available locally if you do not have Internet access in the classroom. For your reference after class, as of this writing, these samples can be found at the following URLs:

    1. http://msdn.microsoft.com/library/en-us/dndotnet/html/remsspi.asp
    2. http://msdn.microsoft.com/library/en-us/dndotnet/html/remsec.asp
    The original announcement from jhawk@microsoft.com can be found in the DOTNET-CLR mailing list archives.

  2. You may have noticed that the installer just unpacks a source tree for each of these samples. You need to install the binaries into the global assembly cache (GAC) to make the lab work. To do this, drill down into the following directories and install the DLL you find in each one into the GAC (ask your instructor if you're not sure how to do this):

    Microsoft\Samples\Security\SSPI\bin
    Microsoft\Samples\Runtime\Remoting\Security\bin
    
  3. Since we're going to be using SSPI directly to secure our remoting channel, we don't need to host in IIS anymore, so go ahead and change your the object URI in the client back to use port 4000, and remove the virtual directory from the URI (it should look something like this: http://localhost:4000/server.rem). If you're working with a partner, please continue to point to your partner's server.

  4. When referencing channel sinks, it's often easiest to put the details of the sink into machine.config, then simply reference it from the various client and server config files on the machine. So bring up machine.config in an editor running with admin privileges, and make the following changes:

    Find the <system.runtime.remoting> element, then find a subelement called <channelSinkProviders>.

    Under the <clientProviders> element, add the following line (order isn't important here, but do not allow any linebreaks in this line):

    <provider id="sspi" type="Microsoft.Samples.Runtime.Remoting.Security.SecurityClientChannelSinkProvider, Microsoft.Samples.Runtime.Remoting.Security, Version=1.0.9.0, Culture=neutral, PublicKeyToken=9f0928b6ea506bde" />
    

    Now find the <serverProviders> element, and add the following line under it:

    <provider id="sspi" type="Microsoft.Samples.Runtime.Remoting.Security.SecurityServerChannelSinkProvider, Microsoft.Samples.Runtime.Remoting.Security, Version=1.0.9.0, Culture=neutral, PublicKeyToken=9f0928b6ea506bde" />
    
  5. Now you need to modify both your client and server's configuration file (named app.config in both projects) to reference the SSPI channel sink you just defined in machine.config. Start with the server - in the server's app.config file, add the following, directly BEFORE the <formatter> element:

          <provider ref='sspi'
                    securityPackage='kerberos'
                    impersonationLevel='impersonate'
                    authenticationLevel='packetPrivacy'/>
    

    In the client's app.config file, add the following directly AFTER the <formatter> element:

          <provider ref='sspi'
                    securityPackage='kerberos'
                    impersonationLevel='impersonate'
                    authenticationLevel='packetPrivacy'/>
    
  6. The current implementation of the sink doesn't set up the managed security context (this is a bug which will be fixed). To work around this bug, you need to make two changes to your server: in the implementation of GetCallerName(), the first line should be changed to look like this:

    IIdentity ident = WindowsIdentity.GetCurrent();

    Secondly, you'll need to comment out the authorization attribute on DoSomethingPrivileged(), as it won't work properly with this bug.

    Finally, in client.cs, make the same change to GetCallerName() as you did in the server.

  7. There's one last thing you need to do in order to use Kerberos. Remember that the client needs to obtain tickets for the server, so the client needs to specify who she expects the server to be running as. This also allows Kerberos to verify that the server is not being spoofed (note that the current implementation of the remoting channel sink doesn't actually use this feature, but it very likely will by the time it's added to the Framework Class Library).

    To set the SPN, you'll need to write a few lines of code in the client to talk to the SSPI channel sink. So add a reference to Microsoft.Samples.Runtime.Remoting.Security.dll (one of the two DLLs you added to the GAC earlier) to your project. You will need to browse for this file, as Visual Studio won't know about it.

    Here's the code you need, along with the using statements you'll need to add at the top of the client.cs file:

    using System.Runtime.Remoting.Channels;
    using Microsoft.Samples.Runtime.Remoting.Security;
    
    // add these two lines right after the call to Activator.GetObject
    IClientSecurity ics = (IClientSecurity)ChannelServices.GetChannelSinkProperties(server)[typeof(SecuritySinkKey)];
    ics.ServerPrincipalName = @"esec\student14";
    

    Note that you'll probably need to change the value of ServerPrincipalName to something other than "esec\student14" - it should be the name of the user you're running the server as (for example, if your partner, esec\student2 launched the server, then it should be "esec\student2").

  8. Build and run your client. You should now see authentication happening in both directions, and if you wire up a trace listener (ask your instructor for help if you don't know what this is) you should not be able to read the arguments being passed back and forth (they are encrypted with the session key).